from random import randint
from OpenGL.GL import *
from OpenGL.GLUT import *
import numpy

from common import *
from common.manager import Manager

class AbstractUnit(AbstractBody):
	"""
	This is the base class for the units in the game.
	"""
	
	SIZE = [1, 1]
	
	# Action states
	IDLE = 0
	MOVING = 1
	ATTACKING = 2
	WORKING = 3
	DYING = 4
	
	# Action animation frames
	#F_IDLE = [(0, 39), (74, 95), (113, 122), (123, 135)]
	F_IDLE = [(0, 39), (113, 122)]
	F_MOVING = [(40, 46)]
	#F_ATTACKING = [(47, 60)]
	F_ATTACKING = [(74, 95)]
	F_WORKING = [(61, 64)]
	F_DYING = [(190, 197)]
	FRAMES = [F_IDLE, F_MOVING, F_ATTACKING, F_WORKING, F_DYING]
	
	def __init__(self, block, team, max_hp, model, height, speed,
				damage, attack_rate, range):
		super(AbstractUnit, self).__init__(AbstractUnit.SIZE, block)
		
		self.team = team
		self.hp = max_hp
		self.max_hp = max_hp
		self.model = model
		self.height = height
		self.speed = speed
		self.action = AbstractUnit.IDLE
		
		self.block = block
		self.position = self.middle
		self.rotation = 90
		
		self.damage = damage
		self.attack_rate = attack_rate
		self.attack_period = 100.0 / attack_rate
		self.attack_counter = 0
		self.range = range
		
		# Movement related attributes.
		self.target = None
		self.attack_target = None
		self.next = None
		self.next_vector = None
		self.move_queue = []
		self.move_t = 0
		
		self.frame = 0
		self.animation_id = 0
		
	def set_middle(self):
		AbstractBody.set_middle(self)
		self.position = numpy.array(self.middle)
		
	def animate(self):
		anim = self.FRAMES[self.action]
		if self.last_frame():
			if self.action == self.DYING:
				Manager.map.remove(self)
			self.animation_id = randint(0, len(anim) - 1)
			self.frame = anim[self.animation_id][0]
		else:
			self.frame += 1
			
	def last_frame(self):
		return self.frame == self.FRAMES[self.action][self.animation_id][1]
			
	def tick(self, dt):
		if self.action == self.MOVING:
			if self.move_t == 10:
				self.block.occupation = None
				self.top_left_block = self.next
				self.block = self.next
				
				if self.attack_target:
					if self.distance(self.attack_target.top_left_block) <= self.range:
						del self.move_queue[:]
						self.set_action(self.ATTACKING)
						self.attack_counter = 0
						return
				
				if len(self.move_queue) == 0:
					self.set_action(self.IDLE)
				
				if self.action == self.MOVING:
					self.next_block_move()
					self.position += self.next_vector
					self.move_t += 1
				else:
					self.middle = self.block.middle
					self.position = numpy.array(self.middle)
			else:
				self.position += self.next_vector
				self.middle = self.position
				self.move_t += 1
				
		elif self.action == self.ATTACKING:
			if not(self.attack_target) or self.attack_target.hp <= 0:
				self.attack_target = None
				self.set_action(self.IDLE)
				return
			
			if self.distance(self.attack_target.top_left_block) > self.range:
				self.move(self.attack_target.top_left_block, False)
			else:
				self.rotate(self.attack_target.top_left_block)
				if self.attack_counter >= self.attack_period:
					Manager.play_sound('shot_click')
					self.attack_target.hp -= self.damage
					if self.attack_target.hp <= 0:
						Manager.play_sound('shot_damage')
						self.attack_target.die()
						self.attack_target = None
					self.attack_counter = 0
				else:
					self.attack_counter += 1
			
	def move(self, target, unset_attack=True):
		if unset_attack: self.attack_target = None
		if self.action == self.IDLE or self.action == self.ATTACKING:
			path = Manager.map.terrain.get_path(self.block, target)
			
			if path:
				self.move_queue += path
				self.next_block_move()
				self.set_action(self.MOVING)
				self.target = path[:-1]
		elif self.action == self.MOVING:
			path = Manager.map.terrain.get_path(self.next, target)
			
			if path:
				del self.move_queue[:]
				self.move_queue += path
				self.target = path[:-1]
		
	def attack(self, target):
		self.attack_target = target
		self.set_action(self.ATTACKING)
		
	def die(self):
		self.block.occupation = None
		if self.next and self.next.occupation == self:
			self.next.occupation = None
			
		self.set_action(self.DYING)
		
	def next_block_move(self):
		next = self.move_queue.pop(0)
#		if not(next.occupation is None):
#			# TODO: Recalcular rota
#			del self.move_queue[:]
#		else:
		next.occupation = self
		self.move_t = 0
		self.next = next
		self.next_vector = (next.middle - self.block.middle) * 0.1
		
		self.rotate(self.next)
	
	def set_action(self, newAction):
		anim = self.FRAMES[newAction]
		
		self.animation_id = randint(0, len(anim) - 1)
		self.frame = anim[self.animation_id][0]
		self.action = newAction
		
	def render(self, alpha=None):
		glPushMatrix()
		glTranslate(*self.position)
		glTranslate(0, 0, self.height * 0.5)
		glScale(0.02, 0.02, 0.02)
		glRotate(self.rotation, 0, 0, 1)
		glEnable(GL_TEXTURE_2D)
		
		if alpha is None: glColor(1, 1, 1)
		else: glColor(1, 1, 1, alpha)
		self.model.render(self.frame, self.team)
		
		glDisable(GL_TEXTURE_2D)
		glPopMatrix()
	
	def render_picking(self):
		self.render()
		pass
	
	def rotate(self, target):
		dx = target.id[X] - self.block.id[X]
		dy = target.id[Y] - self.block.id[Y]
		if dx > 0:
			if dy > 0: self.rotation = 45
			elif dy == 0: self.rotation = 0
			elif dy < 0: self.rotation = -45
		elif dx == 0:
			if dy > 0: self.rotation = 90
			elif dy < 0: self.rotation = -90
		elif dx < 0:
			if dy > 0: self.rotation = 135
			elif dy == 0: self.rotation = 180
			elif dy < 0: self.rotation = -135
	
	def distance(self, other):
		return Manager.map.terrain.distance(self.block, other)
	
class AbstractWorkerUnit(AbstractUnit):
	"""
	This is the base class for all the worker units.
	"""
	
	def __init__(self, block, team, max_hp, model, height, 
				speed, damage, attack_rate, range):
		
		super(AbstractWorkerUnit, self).__init__(block, team, max_hp,
												model, height, speed,
												damage, attack_rate, range)
		
class AbstractBattleUnit(AbstractUnit):
	"""
	This is the base class for all units that can battle.
	"""
	
	def __init__(self, block, team, max_hp, model, height, 
				speed, damage, attack_rate, range):
		
		super(AbstractBattleUnit, self).__init__(block, team, max_hp,
												model, height, speed,
												damage, attack_rate, range)
		